/*
 * Copyright 2023-2025, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * 龙芯LoongArch64架构分页方法实现
 * 本文件实现LoongArch64架构的分页方法
 */

#include "paging/LoongArchPagingMethod.h"

#include <stdlib.h>
#include <string.h>

#include <boot/kernel_args.h>
#include <util/AutoLock.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/VMAddressSpace.h>

#include "LoongArchVMTranslationMap.h"


//#define TRACE_LOONGARCH_PAGING_METHOD_IMPL
#ifdef TRACE_LOONGARCH_PAGING_METHOD_IMPL
#   define TRACE(x...) dprintf(x)
#else
#   define TRACE(x...) ;
#endif


class LoongArchPagingMethodImpl : public LoongArchPagingMethod {
public:
                                LoongArchPagingMethodImpl();
    virtual                     ~LoongArchPagingMethodImpl();

    virtual status_t            Init(kernel_args* args,
                                    VMPhysicalPageMapper** _physicalPageMapper);
    virtual status_t            InitPostArea(kernel_args* args);

    virtual status_t            CreateTranslationMap(bool kernel,
                                    VMTranslationMap** _map);

    virtual status_t            MapEarly(kernel_args* args,
                                    addr_t virtualAddress,
                                    phys_addr_t physicalAddress,
                                    uint8 attributes,
                                    page_num_t (*get_free_page)(kernel_args*));

    virtual bool                IsKernelPageAccessible(addr_t virtualAddress,
                                    uint32 protection);

private:
    phys_addr_t                 fKernelPhysicalPageDirectory;
    loongarch_pte*              fKernelVirtualPageDirectory;
    VMPhysicalPageMapper*       fPhysicalPageMapper;
    TranslationMapPhysicalPageMapper* fKernelPhysicalPageMapper;
};


LoongArchPagingMethodImpl::LoongArchPagingMethodImpl()
    :
    fKernelPhysicalPageDirectory(0),
    fKernelVirtualPageDirectory(NULL),
    fPhysicalPageMapper(NULL),
    fKernelPhysicalPageMapper(NULL)
{
}


LoongArchPagingMethodImpl::~LoongArchPagingMethodImpl()
{
}


status_t
LoongArchPagingMethodImpl::Init(kernel_args* args,
    VMPhysicalPageMapper** _physicalPageMapper)
{
    fKernelPhysicalPageDirectory = args->arch_args.phys_pgd;
    fKernelVirtualPageDirectory = (loongarch_pte*)(addr_t)args->arch_args.vir_pgd;

    // 创建物理页映射器
    // 注意：这里需要实现物理页映射器，类似于x86_64的mapped_physical_page_ops_init
    // 但由于LoongArch64的物理页映射器可能与x86_64不同，这里只是占位
    // 实际实现需要根据LoongArch64的特性来完成
    *_physicalPageMapper = fPhysicalPageMapper;

    return B_OK;
}


status_t
LoongArchPagingMethodImpl::InitPostArea(kernel_args* args)
{
    // 创建一个区域覆盖物理映射区域
    void* address = (void*)KERNEL_PMAP_BASE;
    area_id area = vm_create_null_area(VMAddressSpace::KernelID(),
        "physical map area", &address, B_EXACT_ADDRESS,
        KERNEL_PMAP_SIZE, 0);
    if (area < B_OK)
        return area;

    // 创建一个区域表示内核页目录
    area = create_area("kernel page directory", (void**)&fKernelVirtualPageDirectory,
        B_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED,
        B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
    if (area < B_OK)
        return area;

    return B_OK;
}


status_t
LoongArchPagingMethodImpl::CreateTranslationMap(bool kernel, VMTranslationMap** _map)
{
    LoongArchVMTranslationMap* map = new(std::nothrow) LoongArchVMTranslationMap();
    if (map == NULL)
        return B_NO_MEMORY;

    status_t error = map->Init(kernel);
    if (error != B_OK) {
        delete map;
        return error;
    }

    *_map = map;
    return B_OK;
}


status_t
LoongArchPagingMethodImpl::MapEarly(kernel_args* args, addr_t virtualAddress,
    phys_addr_t physicalAddress, uint8 attributes,
    page_num_t (*get_free_page)(kernel_args*))
{
    TRACE("LoongArchPagingMethodImpl::MapEarly(%#" B_PRIxADDR ", %#" B_PRIxPHYSADDR
        ", %#" B_PRIx8 ")\n", virtualAddress, physicalAddress, attributes);

    // 计算页表项标志
    uint64 flags = 0;
    if ((attributes & B_KERNEL_READ_AREA) != 0)
        flags |= kPteRead;
    if ((attributes & B_KERNEL_WRITE_AREA) != 0)
        flags |= kPteWrite;
    if ((attributes & B_KERNEL_EXECUTE_AREA) != 0)
        flags |= kPteExec;

    // 添加全局页标志和访问/脏位标志
    flags |= kPteGlobal | kPteAccessed | kPteDirty | kPteValid;

    // 调用底层映射函数
    loongarch_pte* pte = LookupPteAlloc(virtualAddress, true, args, get_free_page);
    if (pte == NULL)
        return B_NO_MEMORY;

    // 设置页表项
    pte->val = (physicalAddress & kPtePhysAddrMask) | flags;

    // 刷新TLB
    FlushTLB(virtualAddress);

    return B_OK;
}


bool
LoongArchPagingMethodImpl::IsKernelPageAccessible(addr_t virtualAddress, uint32 protection)
{
    // 检查地址是否在内核空间范围内
    if (virtualAddress >= KERNEL_BASE && virtualAddress <= KERNEL_TOP) {
        // 内核空间地址，允许访问
        return true;
    }

    // 非内核空间地址，不允许访问
    return false;
}


// 创建全局分页方法实例
LoongArchPagingMethod* gLoongArchPagingMethod = new(std::nothrow) LoongArchPagingMethodImpl();